/*********************************************************
  RDD.cpp
  Moment Decision Diagram (MDD) Class
  SMART -- Symbolic Moment Analysis for Rapid Timing
  06-08-2008
  (c) Guoyong Shi;  SJTU
  09-11-2008
  Zhigang Hao; SJTU

  Code Design:
  (1) RDD always computes 1st order moments by default.
  The list of RDD::MomentIn is for accepting the input moments and 
  the list of RDD::MomentOut is for outputing the calculated moments.
  (2) If moments of order higher than 1 (k > 1) are requested, then 
  all moments from moment-1 up to moment-k are computed
  continuously and saved in double arrays.
  (3) If another higher oder is requested, than the rest of moments 
  are computed by using moment-k and saved in more double arrays.
 *********************************************************/

extern "C" {
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>
  //#include <alloc.h>
#include <assert.h>
}


#include "RDD.h"

#include "SmartGlobal.h"
#include "utility.h"

#include <iostream>
#include <iomanip>
#include <stack>
extern int SampleNum;

using namespace std;


// Pass in the C-Tree root.
// RDDsize is equal to the number of C_NODE's in C-Tree.




// All RDD nodes are pointed by the moment pointers.
void RDD::Delete_RDD()
{
  if (MomentIn) {
    delete [] MomentIn;
  }
  if (MomentOut) {
    for (int i = 0; i < RDD_Size; i++) {
      delete (BDD_Node *) Moment[i];
    }
    delete [] MomentOut;
  }
}


// Create RDD from the C-Tree.
void RDD::Create_RDD()
{
  if (!CTree) return;  // do nothing

  // Create RDD from the Zero node.
  Create_RDD_r(CTree, RDD_Zero);
}


// Recursively create the RDD from the C-Tree.
void RDD::Create_RDD_r(C_NODE *ctree, BDD_Node *RDDelse)
{
  BDD_Node *bddnode;
  static int  i = 0;  // the moment index

#ifdef TRACE
  printf("\nCreate_RDD_r() -- C-Tree node [%s]", ctree->elemC->name);
#endif

  if ( ctree == NULL )  // reached a leaf
    return; 

  // Create an RDD node.
  bddnode = new BDD_Node;

  MomentIn[i] = ctree;
  MomentOut[i++] = bddnode;
  // Totally copy the Ctree and the Rtree to MomentIn and MomentOut

  bddnode->Then = ctree; // Let the "Then" edge point to a C-Tree node.
  bddnode->Else = RDDelse;

  //RDDroot->parent = bddnode;
  strcpy(bddnode->name, ctree->elemR->name);
  bddnode->elemR = ctree->elemR;
  bddnode->elemL = ctree->elemL;
  //bddnode->value[SampleNum] = ctree->elemR->value[SampleNum];

#ifdef TRACE
  printf("\n    Created RDDnode [%s]", ctree->elemR->name);
#endif

  // Create a list of BDD nodes that become the parent nodes of RDDroot.
  for (int n = 0;  n < ctree->fanout; n++)
  {
#if 0
    printf("\n... Recur on C-Tree node [%s]",  
        ctree->child[n]->elemC->name);
#endif

    Create_RDD_r(ctree->child[n], bddnode);
  }

}

// Print all BDDs pointed to by the Moment array.
void RDD::Print_Symbolic_Moments()
{
  cout << "\n\nAll symbolic moments: ";
  for (int i = 0; i < RDD_Size && Moment[i]; i++) {
    printf("\n    Moment[%d] = ", i);
    Print_RDD_r( MomentOut[i] );
  }
}


void RDD::Print_RDD_r(BDD_Node *RDDroot)
{
  if (RDDroot == RDD_Zero)
    return;

  printf("%s * %s + ", RDDroot->elemR->name, 
      RDDroot->Then->elemC->name);
  Print_RDD_r(RDDroot->Else);
}


// Evaluate moment[order] using moment[order-1].
// 
void RDD::Evaluate_Moments(int order)
{
  if (order < 1 || order > MaxOrder) {
    cout << "\nEvaluate_Moments(): "
      << "Order out of range; moment not evaluated."
        << endl;
    return;
  }

  if (order == 1) { 
    // First evaluate the C-Tree by accumulating the Caps.
    Graph->Evaluate_CTree(NULL);

    for (int i = 0; i < RDD_Size; i++) {
      //Moment[SampleNum*MaxOrder+0][i] = Evaluate_RDD_r( MomentOut[i] );
      Moment[0][i] = Evaluate_RDD_r( MomentOut[i] );
      if (Graph->Get_Num_Inds() > 0)
        //Moment[SampleNum*MaxOrder+0][RDD_Size+i] = Evaluate_L_Moment_r( MomentOut[i] );
        Moment[0][RDD_Size+i] = Evaluate_L_Moment_r( MomentOut[i] );
    }
  } else if (order > 1) {
    // First evaluate the C-Tree by accumulating the Cap * Moment.
    //Graph->Evaluate_CTree( Moment[SampleNum*MaxOrder+order-2] );
    Graph->Evaluate_CTree( Moment[order-2] );

    for (int i = 0; i < RDD_Size; i++) {
      //Moment[SampleNum*MaxOrder+order-1][i] = Evaluate_RDD_r( MomentOut[i] );
      Moment[order-1][i] = Evaluate_RDD_r( MomentOut[i] );
      if (Graph->Get_Num_Inds() > 0)
        //Moment[SampleNum*MaxOrder+order-1][RDD_Size+i] = Evaluate_L_Moment_r( MomentOut[i] );
        Moment[order-1][RDD_Size+i] = Evaluate_L_Moment_r( MomentOut[i] );
    }
  }
}
// Get the moment for inductance
// Needed by RLC Model order reduction
double RDD::Evaluate_L_Moment_r(BDD_Node *RDDroot)
{
  return RDDroot->Then->SumHigherOrder;
}

// Remember the value in the "Then" pointer is the accumulated capacitance.
// not good for recalculating many times of moments!!!!!!!!!!!
double  RDD::Evaluate_RDD_r(BDD_Node *RDDroot)
{
  double moment;

  if (RDDroot == RDD_Zero)
    return  0.0;
  if (Get_Graph()->Get_Num_Inds() > 0)
    /*
    moment = RDDroot->elemR->value[SampleNum] * RDDroot->Then->SumHigherOrder 
      - RDDroot->elemL->value[SampleNum] * RDDroot->Then->SumLowerOrder 
      + Evaluate_RDD_r(RDDroot->Else);
      */
    moment = RDDroot->elemR->value[1] * RDDroot->Then->SumHigherOrder 
      - RDDroot->elemL->value[1] * RDDroot->Then->SumLowerOrder 
      + Evaluate_RDD_r(RDDroot->Else);
  else
    /*
    moment = RDDroot->elemR->value[SampleNum] * RDDroot->Then->SumHigherOrder 
      + Evaluate_RDD_r(RDDroot->Else);
      */
    moment = RDDroot->elemR->value[1] * RDDroot->Then->SumHigherOrder 
      + Evaluate_RDD_r(RDDroot->Else);

  return  moment;
}



/* Not used, original order should be equal to dump order
// Given a requested moment order (k), all moments
// from order 1 up to k will be calculated and saved.
void RDD::Request_Moment_Order(int order)
{
int maxOrderOld;

#if 00 
// Assert the maximum moment order.
if (order > RDD_Size) {
cout << "\nMoment order exceeding RDD_Size is not necessary.";
cout << "\nMax moment order RDD_Size is used." << endl;
order = RDD_Size;
}
#endif

if (order <= MaxOrder) {
cout << "\nThe moment order you requested already has been computed.";
cout << endl;
return;
}

// Reallocate memory; old contents not changed!!
// Now: order > MaxOrder
Moment = (double **) realloc(Moment, order*sizeof(double*));

maxOrderOld = MaxOrder;
MaxOrder = order;  // save the maximum order

// Now compute those new moments iteratively.
for (int i = maxOrderOld+1; i <= MaxOrder; i++) {
Moment[i-1] = new double[RDD_Size];
Evaluate_Moments(i);
}
}
*/

// Return the moment array of the specified order.
// Moments are counted from order "1" (Elmore delay) up; but
// are saved as Moment[k], k = 0, 1, 2, ...
double* RDD::Get_Moment(int order)
{
  if (order > MaxOrder) {
    cout << "\nRDD::Get_Moment() : MaxOrder exceeded.";
    cout << "\nYou should call Request_Moment_Order(int order) first.";
    cout << endl;
    return  NULL;
  }
  //return  Moment[SampleNum*MaxOrder+order-1];
  return  Moment[order-1];
}


// Dump moments up to order [order] to output.
void RDD::Dump_Moments(int order, ostream &output, int cnt)
{
  int  i, j;
  // Show avoid this during MonteCarlo, because we need to know the
  // max order to store the moment at first.
  if (order > MaxOrder) {
    cout << "Order bigger than MaxOrder" << endl;
    exit(1);
    // Request_Moment_Order(order); 
    // MaxOrder might have been updated there

    // order = MaxOrder;
  }
  output << endl;
  if (cnt == 0)
    output << "Original Circuit Moment" << endl;
  else
    output << "Sample #" << cnt << endl;
  output << setw(10) << "Index";
  output << setw(10) << "Node";
  for (j = 0; j < order; j++) {
    output << setw(10) << "m_c(:," << j+1 << ")";
  }
  if (Graph->Get_Num_Inds() > 0)
    for (j = 0; j < order; j++) {
      output << setw(10) << "m_l(:," << j+1 << ")";
    }
  //#ifdef PRINT_MOMENT
  for (i = 0; i < RDD_Size; i++) {
    output << endl;
    output << setw(10) << i;
    output << setw(10) << Graph->Get_Node_By_Index(i)->name;
    output << "  ";
    for (j = 0; j < order; j++) {
      output << setw(5) << Moment[j][i];
      //output << setw(5) << Moment[SampleNum*MaxOrder+j][i]; 
      // Because of memory allocation order, order of [i], [j] is flipped.
      output << "  ";
    }
    if (Graph->Get_Num_Inds() > 0)
      for (j = 0; j < order; j++) {
        output << setw(5) << Moment[j][RDD_Size+i];
        //output << setw(5) << Moment[SampleNum*MaxOrder+j][RDD_Size+i]; 
        // Because of memory allocation order, order of [i], [j] is flipped.
        output << "  ";
      }
  }
  //#endif
  output << endl;
}

// Dump index to node to output.
void RDD::Dump_IndexToNode(ostream &output)
{
  for (int i = 0; i < RDD_Size; i++) {
    output << endl;
    output << setw(10) << i;
    output << setw(10) << Graph->Get_Node_By_Index(i)->name;
  }
}

RDD::~RDD()
{
  Delete_RDD();

  for (int i = 0; i < MaxOrder; i++) {
    delete [] Moment[i];
  }
  delete [] Moment;
}


RDD::RDD(ToGraph *tograph)
{
  int   i;

  NumNodes = 0;
  NumEdges = 0;
  NumTerms  = 0.0;

  MaxOrder = MAX_ORDER;   // default moment order

  Moment = new double*[MaxOrder];
  //Moment = (double **) calloc(MaxOrder*(MONTECARLO_SAMPLE+1), sizeof(double*));
  CTree = tograph->Get_CTree();
  RDD_Size = tograph->Get_Num_Caps();
  Graph = tograph; // transfer class

  if (RDD_Size > 0) {
    MomentIn  = new C_NODE*[RDD_Size];
    MomentOut = new BDD_Node*[RDD_Size];

    for (i = 0; i < RDD_Size; i++) {
      MomentIn[i]   = NULL;
      MomentOut[i] = NULL;
    }

    // Allocate moment arrays
    //for (i = 0; i < (MaxOrder*(MONTECARLO_SAMPLE+1)); i++) {
    for (i = 0; i < MaxOrder; i++) {
      if (Graph->Get_Num_Inds() > 0) {
        Moment[i] = new double[2*RDD_Size];
      } else {
        Moment[i] = new double[RDD_Size];
      }
    }
  } else {
    MomentIn   = NULL;
    MomentOut = NULL;
  }
  Create_RDD();
}
